﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
using Microsoft.Xrm.Sdk;

namespace AppFx.BizTalk.Dynamics.CRM.Helpers.CRMDataExtract
{
    public class FlattenedXmlFormatter
    {
        public bool PrefixRootEntityAttributes { get; set; }

        public FlattenedXmlFormatter()
        {
            PrefixRootEntityAttributes = false;
        }

        public void Format(Stream stream, Entity entity)
        {
            var xmlWriterSettings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true };
            var xmlWriter = XmlWriter.Create(stream, xmlWriterSettings);

            xmlWriter.WriteStartElement("Entity");

            xmlWriter.WriteElementString("Id", entity.Id.ToString());
            xmlWriter.WriteElementString("LogicalName", entity.LogicalName);
            if (entity.EntityState.HasValue)
                xmlWriter.WriteElementString("EntityState", Enum.GetName(typeof(EntityState), entity.EntityState.Value));

            //Attributes
            xmlWriter.WriteStartElement("Attributes");
            if (entity.Attributes != null)
            {
                foreach (var attribute in entity.Attributes)
                {
                    var value = attribute.Value as AliasedValue;
                    if (value != null)
                    {
                        FormatAliasedValue(xmlWriter, value);
                    }
                    else
                    {
                        FormatAttributeValue(xmlWriter, attribute, entity.LogicalName);
                    }
                }
            }
            xmlWriter.WriteEndElement(); //Attributes

            //FormattedValues
            xmlWriter.WriteStartElement("FormattedValues");
            if (entity.FormattedValues != null)
            {
                foreach (var formattedValue in entity.FormattedValues)
                {
                    xmlWriter.WriteElementString(formattedValue.Key, Convert.ToString(formattedValue.Value));
                }
            }
            xmlWriter.WriteEndElement(); //FormattedValues

            xmlWriter.WriteEndElement(); //Entity
            xmlWriter.Flush();
            xmlWriter.Close();
        }

        private static void FormatAliasedValue(XmlWriter xmlWriter, AliasedValue crmAliasedValue)
        {
            var elementName = string.Format("{0}.{1}", crmAliasedValue.EntityLogicalName,
                crmAliasedValue.AttributeLogicalName);

            if (crmAliasedValue.Value is EntityReference)
            {
                var entityReference = (EntityReference) crmAliasedValue.Value;
                xmlWriter.WriteElementString(elementName, entityReference.Name);
            }
            else if (crmAliasedValue.Value is string)
            {
                var stringValue = crmAliasedValue.Value as string;
                xmlWriter.WriteElementString(elementName, stringValue);
            }
            else if (crmAliasedValue.Value is DateTime)
            {
                var dtValue = crmAliasedValue.Value as DateTime?;
                xmlWriter.WriteElementString(elementName, dtValue.ToString());
            }
            else if (crmAliasedValue.Value is OptionSetValue)
            {
                var optionSetValue = crmAliasedValue.Value as OptionSetValue;
                xmlWriter.WriteElementString(elementName, optionSetValue.Value.ToString(CultureInfo.InvariantCulture));
            }
            else if (crmAliasedValue.Value is Guid)
            {
                var guidValue = crmAliasedValue.Value as Guid?;
                xmlWriter.WriteElementString(elementName, guidValue.ToString());
            }
            else if (crmAliasedValue.Value is Money)
            {
                var moneyValue = crmAliasedValue.Value as Money;
                xmlWriter.WriteElementString(elementName, Convert.ToString(moneyValue.Value, CultureInfo.InvariantCulture));
            }
            else
            {
                xmlWriter.WriteElementString(elementName, Convert.ToString(crmAliasedValue.Value.ToString()));
            }
        }

        private void FormatAttributeValue(XmlWriter xmlWriter, KeyValuePair<string, object> crmAttribute, string crmLogicalEntityName)
        {
            var elementName = crmAttribute.Key;

            if (PrefixRootEntityAttributes)
                elementName = string.Format("{0}.{1}", crmLogicalEntityName, crmAttribute.Key);

            if (crmAttribute.Value is EntityReference)
            {
                var entityReference = (EntityReference) crmAttribute.Value;
                xmlWriter.WriteElementString(elementName, entityReference.Name);
            }
            else if (crmAttribute.Value is string)
            {
                var stringValue = (string) crmAttribute.Value;
                xmlWriter.WriteElementString(elementName, stringValue);
            }
            else if (crmAttribute.Value is DateTime)
            {
                var dtValue = crmAttribute.Value as DateTime?;
                xmlWriter.WriteElementString(elementName, dtValue.ToString());
            }
            else if (crmAttribute.Value is OptionSetValue)
            {
                var optionSetValue = (OptionSetValue) crmAttribute.Value;
                xmlWriter.WriteElementString(elementName, optionSetValue.Value.ToString(CultureInfo.InvariantCulture));
            }
            else if (crmAttribute.Value is Guid)
            {
                var guidValue = crmAttribute.Value as Guid?;
                xmlWriter.WriteElementString(elementName, guidValue.ToString());
            }
            else if (crmAttribute.Value is Money)
            {
                var moneyValue = (Money) crmAttribute.Value;
                xmlWriter.WriteElementString(elementName, Convert.ToString(moneyValue.Value, CultureInfo.InvariantCulture));
            }
            else
            {
                xmlWriter.WriteElementString(elementName, Convert.ToString(crmAttribute.Value.ToString()));
            }
        }
    }
}

